package agent {
import agent.SeleFxAgent;
import automation.*;

import mx.automation.IAutomationClass;
import mx.automation.IAutomationEnvironment;
import mx.automation.IAutomationEventDescriptor;
import mx.automation.IAutomationMethodDescriptor;
import mx.automation.IAutomationObject;

import mx.controls.Alert;

import util.EnvXMLParser;

public class SeleFxEnvironment implements IAutomationEnvironment {

    private var classInfoNameMap:Object = {};
    private var classInfoImplMap:Object = {};

    public function SeleFxEnvironment(source:XML) {
        super();

        var parser:EnvXMLParser = new EnvXMLParser();
        var outputXML:XML = parser.parse(source);
        mapXMLToAutomation(outputXML);
    }

    private function mapXMLToAutomation(source:XML):void {
        for (var i:Object in source.ClassInfo) {
            var classInfoXML:XML = source.ClassInfo[i];
            var classInfoName:String = classInfoXML.@Name.toString();
            var superClassName:String =
                    classInfoXML.@Extends && classInfoXML.@Extends.length != 0 ?
                    classInfoXML.@Extends.toString() :
                    null;
            var selefxAutomation:SeleFxAutomation = new SeleFxAutomation(classInfoName, superClassName);
            classInfoNameMap[classInfoName] = selefxAutomation;

            for (var j:Object in classInfoXML.Implementation) {
                var implementationXML:XML = classInfoXML.Implementation[j];
                var classInfoImpl:String =
                        implementationXML.@Class.toString().replace("::", ".");
                classInfoImplMap[classInfoImpl] = selefxAutomation;
                selefxAutomation.addImplementationClassName(classInfoImpl);
            }

            var pd:SeleFxPropertyDescriptor;

            // for every Method
            for (var k:Object in classInfoXML.Methods.Method) {
                var methodXML:XML = classInfoXML.Methods.Method[k];

                var classInfoMethod:String = methodXML.@Name.toString();
                var classInfoMethodImpl:String =
                        methodXML.Implementation.@Type.toString();

                if (classInfoMethodImpl)
                {
                    var parameters:Array = [];
                    for (var m:Object in methodXML.Parameter)
                    {
                        var parameterXML:XML = methodXML.Parameter[m];
                        var parameterName:String = parameterXML.@Name.toString();
                        var parameterType:String =
                                parameterXML.PropertyType.@Type.toString().toLowerCase();
                        pd = new SeleFxPropertyDescriptor(parameterName, true, true, parameterType, null);
                        parameters.push(pd);
                    }

                    var returnType:String =
                            methodXML.ReturnValueType.PropertyType.@Type.toString();

                    var codecName:String =
                            methodXML.ReturnValueType.Type.@Codec.toString();

                    var md:IAutomationMethodDescriptor = new SeleFxMethodDescriptor(
                            classInfoMethod, classInfoMethodImpl, returnType, codecName, parameters);
                    selefxAutomation.addMethod(md);
                }
            }


            // for each Event
            for (var q:Object in classInfoXML.Events.Event) {

                var eventXML:XML = classInfoXML.Events.Event[q];

                var automationEventName:String = eventXML.@Name.toString();
                var eventClassName:String =
                        eventXML.Implementation.@Class.toString();
                eventClassName = eventClassName.replace("::", ".");
                var eventType:String =
                        eventXML.Implementation.@Type.toString();

                if (eventType)
                {
                    var properties:Array = [];
                    for (var n:Object in eventXML.Property)
                    {
                        var eventPropertyXML:XML = eventXML.Property[n];
                        var eventPropertyName:String = eventPropertyXML.@Name.toString();
                        var eventPropertyType:String =
                                eventPropertyXML.PropertyType.@Type.toString().toLowerCase();
                        var eventPropertyCodec:String = eventPropertyXML.PropertyType.@Codec.toString();
                        var defaultValue:String =
                                eventPropertyXML.@DefaultValue.length() > 0 ?
                                eventPropertyXML.@DefaultValue.toString() :
                                null;
                        pd = new SeleFxPropertyDescriptor(
                                eventPropertyName, true, true, eventPropertyType,
                                (eventPropertyCodec == null || eventPropertyCodec.length == 0 ?
                                 "object" :
                                 eventPropertyCodec), defaultValue);
                        properties.push(pd);
                    }

                    var ed:IAutomationEventDescriptor = new SeleFxEventDescriptor(
                            automationEventName, eventClassName,
                            eventType, properties);
                    selefxAutomation.addEvent(ed);
                }
            }

            // for each Property
            for (var p:Object in classInfoXML.Properties.Property) {
                var propertyXML:XML = classInfoXML.Properties.Property[p];
                var propName:String = propertyXML.@Name.toString();
                var propType:String =
                        propertyXML.PropertyType.@Type.toString().toLowerCase();
                var propCodec:String = propertyXML.Type.@Codec.toString();
                pd = new SeleFxPropertyDescriptor(
                        propName,
                        propertyXML.@ForDescription.toString() == "true",
                        propertyXML.@ForVerification.toString() == "true",
                        propType,
                        (propCodec == null || propCodec.length == 0 ?
                         "object" :
                         propCodec));
                selefxAutomation.addPropertyDescriptor(pd);
            }
        }
    }

    public function getAutomationClassByName(className:String):IAutomationClass {
        return SeleFxAutomation(classInfoNameMap[className]);
    }

    public function getAutomationClassByInstance(obj:IAutomationObject):IAutomationClass {
        var result:IAutomationClass = findClosestAncestor(obj);
        if (! result) {
            throw new Error("Automtaion Class Not Found : " + SeleFxAutomation.getClassName(obj));
        }
        return result;
    }

    private function findClosestAncestor(obj:Object):IAutomationClass {
        var className:String = SeleFxAutomation.getClassName(obj);
        if (className in classInfoImplMap)
            return classInfoImplMap[className];

        var ancestors:Array = findAllAncestors(obj);
        if (ancestors.length != 0) {
            classInfoImplMap[className] = ancestors[0];
            return classInfoImplMap[className];
        } else {
            return null;
        }
    }

    private function findAllAncestors(obj:Object):Array {
        var result:Array = [];

        for (var i:String in classInfoImplMap) {
            var realClass:Class =
                    SeleFxAutomation.getDefinitionFromObjectDomain(obj, i);
            if (realClass && obj is realClass)
                result.push(classInfoImplMap[i]);
        }

        result.sort(sortAncestors);

        return result;
    }

    private function sortAncestors(a:IAutomationClass, b:IAutomationClass):int {
        var superClass:IAutomationClass;
        var x:String = a.superClassName;
        while (x) {
            if (x == b.name)
                return -1;
            superClass = getAutomationClassByName(x);
            x = superClass.superClassName;
        }

        x = b.superClassName;
        while (x) {
            if (x == a.name)
                return 1;
            superClass = getAutomationClassByName(x);
            x = superClass.superClassName;
        }

        return 0;
    }
}
}